package com.iqiyi.pps.epg.core.service.carousel;


import com.google.common.collect.Lists;
import com.iqiyi.kiwi.utils.Configuration;
import com.iqiyi.pps.epg.api.model.web.carousel.*;
import com.iqiyi.pps.epg.api.model.web.query.TFilter;
import com.iqiyi.pps.epg.api.model.web.query.TPage;
import com.iqiyi.pps.epg.core.dao.base.Page;
import com.iqiyi.pps.epg.core.dao.base.PropertyFilter;
import com.iqiyi.pps.epg.core.dao.carousel.AutoCarouselEpisodeLibraryDao;
import com.iqiyi.pps.epg.core.dao.carousel.AutoCarouselEpisodeRuleDao;
import com.iqiyi.pps.epg.core.dao.carousel.CarouselBaseDao;
import com.iqiyi.pps.epg.core.dao.carousel.IIndexSortDao;
import com.iqiyi.pps.epg.core.dao.channel.ChannelLogDao;
import com.iqiyi.pps.epg.core.dao.program.ProgramDataDao;
import com.iqiyi.pps.epg.core.dao.remind.MailRemindDao;
import com.iqiyi.pps.epg.core.model.carousel.*;
import com.iqiyi.pps.epg.core.model.channel.ChannelBase;
import com.iqiyi.pps.epg.core.model.channel.ChannelConfig;
import com.iqiyi.pps.epg.core.model.channel.SearchSettingConfig;
import com.iqiyi.pps.epg.core.model.program.ProgramData;
import com.iqiyi.pps.epg.core.model.remind.MailRemind;
import com.iqiyi.pps.epg.core.rpc.qipu.QipuService;
import com.iqiyi.pps.epg.core.service.channel.ChannelService;
import com.iqiyi.pps.epg.core.service.notifymail.NotifyMailService;
import com.iqiyi.pps.epg.core.service.program.IProgramAsyncService;
import com.iqiyi.pps.epg.core.utils.*;
import com.iqiyi.pps.epg.core.utils.mail.MailConstant;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.type.StringType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by Lemuel on 2015/6/5.
 */
@Service
@Transactional
public class CarouselServiceImpl implements CarouselService {
    public static Logger logger = LoggerFactory.getLogger(CarouselServiceImpl.class);
    private static final int ONCE_FIND = 50;
    public static final int MAX_ERROR_COUNT = 2;
    private static final int VALID_NUM = 12 * 24;
    @Autowired
    private CarouselBaseDao carouselBaseDao;

    @Autowired
    private AutoCarouselEpisodeLibraryDao autoCarouselEpisodeLibraryDao;

    @Autowired
    private AutoCarouselEpisodeRuleDao carouselAutomationDao;

    @Autowired
    private ProgramDataDao programDataDao;
    @Autowired
    private QipuService qipuService;
    @Autowired
    private ChannelService channelService;
    @Autowired
    private ChannelLogDao channelLogDao;
    @Autowired
    private MailRemindDao mailRemindDao;

    @Autowired
    private NotifyMailService notifyMailService;

    @Autowired
    private IProgramAsyncService programAsyncService;

    private long lastReadCreateDayTime = 0;
    private long readGap = 300000;
    private int carouselCreateDay = 0;

    private Map<Long, Thread> syncTaskList = new HashMap<>();

    @Override
    public void saveBase(CarouselBase entity) {
        carouselBaseDao.saveOnly(entity);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public CarouselBase getCarouselBase(long id) {
        return carouselBaseDao.get(id);
    }

    @Override
    public boolean delCarousel(String ids) {
        String sql = "update carousel_episode_rule set status=" + CarouselBase.DEL_STATUS + " where id in (" + ids + ")";
        try {
            carouselBaseDao.getSession().createSQLQuery(sql).executeUpdate();
        } catch (Exception e) {
            logger.info("[CarouselServiceImpl][delCarousel][Exception={}]", e);
            return false;
        }
        return true;
    }

    @Override
    public TCarouselPage getCarouselList(TPage page, TFilter filter) {
        TCarouselPage data = new TCarouselPage();

        try {
            long startTime = System.currentTimeMillis();

            String tempPattern = "${PARAMS}";
            String resultPattern = "*";
            String totalPattern = "COUNT(*) as total";
            String listSql = "SELECT " + tempPattern + " FROM carousel_episode_rule WHERE 1 ";

            List<PropertyFilter> filterList = TransUtil.builderFilterList(filter);

            Page<CarouselBase> dbPage = TransUtil.buildPage(page);

            String sql = SqlUtil.buildQueryStringByPropertyFilter(dbPage, listSql, filterList);

//            String orderSql = " order by IF(carousel_episode_rule.copyright_end_time = 0 ||" +
//                    "carousel_episode_rule.copyright_end_time = -100 ,0,1),carousel_episode_rule.copyright_end_time desc";
            String orderSql = " order by repeated_status, end_time DESC";


            if (dbPage.isAutoCount()) {
                String totalCount = carouselBaseDao.getSession().createSQLQuery(sql.replace(tempPattern, totalPattern))
                        .addScalar("total", new StringType()).list().get(0).toString();

                page.setTotalCount(Long.valueOf(totalCount));
            }

            if (!sql.contains("order by")) {
                sql += orderSql;
            }

            sql += " LIMIT " + (dbPage.getPageNo() - 1) * dbPage.getPageSize() + " , " + dbPage.getPageSize();
            List result = carouselBaseDao.getSession().createSQLQuery(sql.replace(tempPattern, resultPattern))
                    .addEntity("base", CarouselBase.class)
                    .list();

            List<TCarousel> carouselList = new ArrayList<>();

            for (Object single : result) {
                CarouselBase carouselBase = (CarouselBase) single;

                TCarousel carousel = transToTCarousel(carouselBase);

                carouselList.add(carousel);
            }

            data.setResult(carouselList);
            data.setTPage(page);

            logger.info("CarouselServiceImpl.getCarouselList(page, filter): {}", System.currentTimeMillis() -
                    startTime);

            return data;

        } catch (Exception e) {
            logger.error("[CarouselServiceImpl][getCarouselList][exception=]", e);
        }

        return data;
    }

    public TCarouselPage getList(TPage page, TFilter filter) {
        List<PropertyFilter> filterList = TransUtil.builderFilterList(filter);
        Page<CarouselBase> dbPage = TransUtil.buildPage(page);
        Page<CarouselBase> pageData = queryCarouselBaseByFilters(dbPage, filterList);
        List<CarouselBase> dataList = pageData.getResult();
        TCarouselPage carouselPage = new TCarouselPage();
        page.setTotalCount(pageData.getTotalCount());
        carouselPage.setTPage(page);

        List<TCarousel> tCarouselList = transToTCarouselList(dataList);
        carouselPage.setResult(tCarouselList);
        return carouselPage;
    }

    public TAutomationPage getAutomationList(TPage page, TFilter filter) {
        List<PropertyFilter> filterList = TransUtil.builderFilterList(filter);
        Page<AutoCarouselEpisodeRule> dbPage = TransUtil.buildPage(page);
        Page<AutoCarouselEpisodeRule> pageData = carouselAutomationDao.findPage(dbPage, filterList);
        List<AutoCarouselEpisodeRule> dataList = pageData.getResult();
        TAutomationPage tAutomationPage = new TAutomationPage();
        page.setTotalCount(pageData.getTotalCount());
        tAutomationPage.setTPage(page);
        List<TCarouselAutomation> tCarouselList = transToTCarouselAutomationList(dataList);
        tAutomationPage.setResult(tCarouselList);
        return tAutomationPage;
    }

    public Page<CarouselBase> queryCarouselBaseByFilters(Page<CarouselBase> page, List<PropertyFilter> filters) {
        return carouselBaseDao.findPage(page, filters);
    }

    public Page<AutoCarouselEpisodeLibrary> queryCarouselEpisodeByFilters(Page<AutoCarouselEpisodeLibrary> page, List<PropertyFilter> filters) {
        return autoCarouselEpisodeLibraryDao.findPage(page, filters);
    }

    private List<TCarouselEpisode> transToTCarouselEpisodeList(List<AutoCarouselEpisodeLibrary> dataList) {
        List<TCarouselEpisode> resultList = new ArrayList<>();
        TCarouselEpisode tCarouselEpisode = null;
        for (AutoCarouselEpisodeLibrary tempData : dataList) {
            tCarouselEpisode = transToTCarouselEpisode(tempData);
            resultList.add(tCarouselEpisode);
        }
        return resultList;
    }

    private List<TCarousel> transToTCarouselList(List<CarouselBase> dataList) {
        List<TCarousel> resultList = new ArrayList<>();
        TCarousel tCarousel = null;
        for (CarouselBase tempData : dataList) {
            tCarousel = transToTCarousel(tempData);
            resultList.add(tCarousel);
        }
        return resultList;
    }

    private TCarousel transToTCarousel(CarouselBase tempData) {
        TCarousel _TCarousel = new TCarousel();
        _TCarousel.setId(tempData.getId());
        _TCarousel.setFatherId(tempData.getFatherId());
        _TCarousel.setQipuId(tempData.getQipuId());
        _TCarousel.setStatus(tempData.getStatus());
        _TCarousel.setShowName(tempData.getShowName());
        _TCarousel.setStartTime(DateTimeUtil.transLongFormat(tempData.getStartTime().getTime()));
        _TCarousel.setEndTime(DateTimeUtil.transLongFormat(tempData.getEndTime().getTime()));
        _TCarousel.setItemTime(tempData.getItemTime());
        _TCarousel.setStartNum(tempData.getStartNum());
        _TCarousel.setFollowNum(tempData.getFollowNum());
        _TCarousel.setTotalNum(tempData.getTotalNum());
        _TCarousel.setRepeatedStatus(tempData.getRepeatedStatus());
        _TCarousel.setFollowedStatus(tempData.getFollowedStatus());
        _TCarousel.setChannelTag(tempData.getChannelTag());
        _TCarousel.setCopyrightEndTime(0 == tempData.getCopyrightEndTime() ? CarouselBase.DEFAULT_COPYRIGHT_END_TIME_FORMAT
                : DateTimeUtil.transLongFormat(tempData.getCopyrightEndTime()));
        _TCarousel.setBossStatus(tempData.getBossStatus());
        _TCarousel.setPublishStatus(tempData.getPublishStatus());
        _TCarousel.setCreatedTime(DateTimeUtil.transLongFormat(tempData.getCreatedTime().getTime()));
        _TCarousel.setOpUser(tempData.getOpUser());
        _TCarousel.setPageUrl(tempData.getPageUrl());
        _TCarousel.setActualNum(tempData.getActualNum());
        _TCarousel.setServerTime(DateTimeUtil.transLongFormat(System.currentTimeMillis()));
        return _TCarousel;
    }

    private TCarouselEpisode transToTCarouselEpisode(AutoCarouselEpisodeLibrary tempData) {
        TCarouselEpisode _TCarouselEpisode = new TCarouselEpisode();
        _TCarouselEpisode.setId(tempData.getId());
        _TCarouselEpisode.setFatherId(tempData.getFatherId());
        _TCarouselEpisode.setQipuId(tempData.getQipuId());
        _TCarouselEpisode.setStatus(tempData.getStatus());
        _TCarouselEpisode.setAlbumId(tempData.getAlbumId());
        _TCarouselEpisode.setAlbumName(tempData.getAlbumName());
        _TCarouselEpisode.setChannelName(tempData.getChannelName());
        _TCarouselEpisode.setCopyrightEndTime(tempData.getCopyrightEndTime());
        _TCarouselEpisode.setCreatedTime(DateTimeUtil.transLongFormat(tempData.getCreatedTime().getTime()));
        _TCarouselEpisode.setDuration(tempData.getDuration());
        _TCarouselEpisode.setExecuteIndex(tempData.getExecIndex());
        _TCarouselEpisode.setLastSortTime(tempData.getLastSortTime());
        _TCarouselEpisode.setExecuteResult(tempData.getExecResult());
        _TCarouselEpisode.setOpUser(tempData.getOpUser());
        _TCarouselEpisode.setGlobalPublishDate(tempData.getGlobalPublishDate());
        _TCarouselEpisode.setPagePublishStatus(tempData.getPagePublishStatus());
        _TCarouselEpisode.setProgramName(tempData.getProgramName());
        _TCarouselEpisode.setTwAlbumName(tempData.getTwAlbumName());
        _TCarouselEpisode.setTwProgramName(tempData.getTwProgramName());
        _TCarouselEpisode.setTwChannelName(tempData.getTwChannelName());
        return _TCarouselEpisode;
    }

    public void addErrorCount(CarouselBase entity) {
        int updateNum = carouselBaseDao.addErrorCount(entity.getId());
        logger.info("[addErrorCount] updateNum = {},id:{}", updateNum, entity.getId());
        if (updateNum > 0) {
            if (entity.getErrorCount() + 1 >= MAX_ERROR_COUNT) {
                doSendErrorCountMail(entity);
            }
        }
    }

    public void doSendErrorCountMail(CarouselBase entity) {
        ChannelBase channelBase = channelService.getChannelBase(entity.getFatherId());
        Map<String, String> map = new HashMap<String, String>();
        map.put(MailConstant.C_NAME, channelBase.getName());
        map.put(MailConstant.C_ID, "" + channelBase.getId());
        map.put(MailConstant.C_QIPU_ID, "" + channelBase.getQipuId());
        map.put(MailConstant.SHOW_NAME, entity.getShowName());
        map.put(MailConstant.CAROUSEL_QIPU_ID, "" + entity.getQipuId());
        notifyMailService.addMailAsyncTask(MailConstant.TOS_TYPE_P_MAIL_TYPE, MailContentUtil.ContentType.CAROUSEL_ERROR_COUNT_REMIND.getTypeIndex(), map);
    }

    @Override
    public void executeRules(final CarouselBase entity, String createDays, String createStartTime) {

        final String ruleStartTime = StringUtils.isBlank(createStartTime) ? DateTimeUtil.transLongFormat(entity
                .getStartTime().getTime()) : createStartTime;

        final int ruleDays = isPositiveInteger(createDays) ? Integer.parseInt
                (createDays) : getCarouselCreateDay();

        try {
            Thread syncTask = new Thread(new Runnable() {
                @Override
                public void run() {
                    logger.info("[CarouselServiceImpl][executeRules][syncTask][start with id = " + entity.getId() +
                            "]");
                    boolean result = false;
                    ChannelBase channelBase = channelService.getChannelBase(entity.getFatherId());
                    List<ProgramQipuData> dataList = null;
                    try {
                        dataList = CarouselAssistUtil.create(entity, channelBase, ruleStartTime, ruleDays);
                        if (entity.getStatus() == CarouselBase.ILLEGAL_STATUS && ruleDays == getCarouselCreateDay()) {
                            carouselBaseDao.save(entity);
                            syncTaskList.remove(entity.getId());
                            return;
                        }
                    } catch (Exception e) {
                        logger.error("[CarouselServiceImpl] excute create fail {}", e);
                        logger.info("[CarouselServiceImpl][executeRules][syncTask][end with id = " + entity.getId() + "]");
                        addErrorCount(entity);
                        syncTaskList.remove(entity.getId());
                        return;
                    }

//                    List<ProgramQipuData> data = testProgramQipuDataList();

                    logger.info("[CarouselServiceImpl][executeRules][CarouselAssistUtil.create]  end");
                    if (null == dataList) {
                        logger.info("[CarouselServiceImpl][executeRules][syncTask][failed with id = " + entity.getId
                                () + "]");
                        addErrorCount(entity);
                        return;
                    }

                    List<ProgramData> saveList = new ArrayList<>();

                    for (ProgramQipuData tempData : dataList) {
                        saveList.add(transferQipuData(tempData));
                    }
                    int saveLength = saveList.size();
                    if (saveLength > 0) {
                        result = saveProgramDataList(saveList, entity.checkIsFollow(), entity.getFatherId());
                        if (result) {
                            channelService.notifyHasPrograms(entity.getFatherId());
                            Session session = carouselBaseDao.getSessionFactory().openSession();
                            Transaction transaction = session.beginTransaction();
                            try {
                                CarouselBase carouselBase = (CarouselBase) session.get(CarouselBase.class, entity.getId());
                                if (null != carouselBase) {
                                    Calendar calendar = Calendar.getInstance();
                                    calendar.setTimeInMillis(carouselBase.getExtendTime().getTime());

                                    // extend_time为null代表初次创建七天规则时更新extend_time到开始时间的六天后
                                    if (carouselBase.getCreatedTime().getTime() != carouselBase.getExtendTime().getTime()) {
                                        calendar.add(Calendar.DATE, ruleDays);
                                    } else {
                                        calendar.add(Calendar.DATE, ruleDays - 1);
                                    }


                                    logger.info("[CarouselServiceImpl][executeRules][syncTask][update][list_first={}]", saveList.get(0).getStartPlayTime().getTime());
                                    logger.info("[CarouselServiceImpl][executeRules][syncTask][update][extend_time={}]", saveList.get(saveLength - 1).getStartPlayTime().getTime());
                                    carouselBase.setExtendTime(new Timestamp(saveList.get(saveLength - 1).getStartPlayTime().getTime()));

                                    if (entity.isNeedUpdate()) {
                                        carouselBase.setActualNum(entity.getActualNum());
                                        if (carouselBase.getItemTime() != entity.getItemTime() && entity.getItemTime() > 0) {
                                            carouselBase.setItemTime(entity.getItemTime());
                                        }
                                    }
                                    if (!entity.checkIsFollow()) {
                                        carouselBase.setLastCreatedNo(entity.getLastCreatedNo());
                                        carouselBase.setLastCreatedQipuId(entity.getLastCreatedQipuId());
                                    }

                                    session.save(carouselBase);
                                    transaction.commit();
                                } else {
                                    logger.info("[CarouselServiceImpl][executeRules][syncTask][failed with id = " + entity.getId());
                                }
                            } catch (Exception e) {
                                logger.error(e.getMessage());
                                transaction.rollback();
                                logger.info("[CarouselServiceImpl][executeRules][syncTask][failed with id = " + entity.getId());
                            } finally {
                                session.close();
                            }
                        } else {
                            logger.info("[CarouselServiceImpl][executeRules][syncTask] no new Data ");
                        }
                    } else {
                        logger.info("[CarouselServiceImpl][executeRules][syncTask][end with id = " + entity.getId() + "]");
                    }
                    logger.info("[CarouselServiceImpl][executeRules][syncTask][end with id = " + entity.getId() + "]");

                    syncTaskList.remove(entity.getId());
                }
            });

            syncTaskList.put(entity.getId(), syncTask);

            syncTask.start();

        } catch (Exception e) {
            logger.error("[CarouselServiceImpl][executeRules][exception=]", e);
        }
    }

    private ProgramData transferQipuData(ProgramQipuData data) {
        return data.getOriginal();
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    private boolean saveProgramDataList(List<ProgramData> saveList, boolean skipQipuId, long channelId) {
        int addLength = saveList.size();
        if (addLength > 0) {
            logger.info("[dealResponseList]  start fill qipuId");
            List<Long> qipuIdList = null;
            if (!skipQipuId) {
                qipuIdList = qipuService.registerQipuId(23, addLength);
            }
            ProgramData entity = null;
            ChannelBase father = channelService.getChannelBase(channelId);
            String formatString = getFormatString(father);
            if (!skipQipuId) {
                for (int i = 0; i < addLength; i++) {
                    entity = saveList.get(i);
                    if (entity.checkIsIllegal()) {
                        continue;
                    }
                    entity.setQipuId(qipuIdList.get(i));
                    ProgramData.calPlayStatus(entity);
                    if (formatString != null) {
                        entity.setPageUrl(String.format(formatString, UrlGeneratorUtil.generateUrl(entity.getQipuId())));
                    }
                }
            }
            logger.info("[dealResponseList]  end fill qipuId");
        }

        Session session = programDataDao.getSessionFactory().openSession();
        Transaction transaction = session.beginTransaction();
        try {
            for (ProgramData temp : saveList) {
                session.save(temp);
            }
            transaction.commit();
        } catch (Exception e) {
            logger.error(e.getMessage());
            transaction.rollback();

            return false;
        } finally {
            session.close();
        }

        return true;
    }

    private String getFormatString(ChannelBase tempBase) {
        String formatString = null;
        if (tempBase.getLiveType() == 2) {
            formatString = "http://www.iqiyi.com/l_%s.html";
        } else if (tempBase.getLiveType() == 1 || tempBase.getLiveType() == 3) {
            if (tempBase.getPageUrl() != null && tempBase.getPageUrl().startsWith("http")) {
                formatString = tempBase.getPageUrl() + "?l=%s";
            } else {
                formatString = UrlGeneratorUtil.getChannelPageUrl(tempBase.getQipuId(), tempBase.getLiveType()) + "?l=%s";
            }
        }
        return formatString;
    }

    private static boolean isPositiveInteger(String checkValue) {
        if (checkValue == null) {
            return false;
        }
        return checkValue.matches("[1-9]\\d*");
    }

//    private List<ProgramQipuData> testProgramQipuDataList() {
//        List<ProgramQipuData> programQipuDataList = new ArrayList<>();
//
//        ProgramQipuData programQipuData = new ProgramQipuData();
//
//        ProgramData programData = programQipuData.getOriginal();
//
//        programData.setFatherId(1000);
//        programData.setQipuId(7000000000L);
//        programData.setType(1);
//        programData.setCarouselEpisode(0L);
//        programData.setLive(0);
//        programData.setStartPlayTime(new Timestamp(System.currentTimeMillis()));
//        programData.setStopPlayTime(new Timestamp(System.currentTimeMillis()));
//        programData.setPname("TEST CAROUSEL");
//        programData.setMemberOnly(0);
//        programData.setMemberFree(0);
//        programData.setAllowComment(0);
//        programData.setQiyiProduct(0);
//        programData.setQiyiExclusive(0);
//
//
//        programQipuDataList.add(programQipuData);
//
//        return programQipuDataList;
//    }

    public int getCarouselCreateDay() {
        long current = System.currentTimeMillis();
        if (current > lastReadCreateDayTime + readGap) {
            List<ChannelConfig> list = channelService.getConfig("carousel_create");
            carouselCreateDay = Integer.valueOf(list.get(0).getVal());
            lastReadCreateDayTime = current;
        }
        return carouselCreateDay;
    }

    public int delByChannelId(long channelId) {
        String hql = "update CarouselBase set status = -1 where fatherId=? and status = 1";
        int executeNum = carouselBaseDao.batchExecute(hql, channelId);
        logger.info("hql delByChannelId , [affectNum ={}][channelId={}]", executeNum, channelId);
        return executeNum;
    }

    public int delAutomationByChannelId(long channelId) {
        String hql = "update AutoCarouselEpisodeRule set status = -1 where fatherId = ? ";
        int executeNum = carouselAutomationDao.batchExecute(hql, channelId);
        logger.info("hql delAutomationByChannelId , [affectNum ={}][channelId={}]", executeNum, channelId);
        return executeNum;
    }

    @Override
    public List<CarouselBase> getCarouselToUpdate() {
//        List<CarouselBase> retList = Lists.newArrayList();
        List<PropertyFilter> filters = Lists.newArrayList();
        Calendar programCalendar = Calendar.getInstance();
        long current = System.currentTimeMillis();
//        programCalendar.add(Calendar.DATE, CarouselBase.DEFAULT_CREATE_DAYS - 1);    //时间标的7天后0点，包括规则创建的那天，所以要-1
        programCalendar.add(Calendar.DATE, getCarouselCreateDay() - 1);    //时间标的7天后0点，包括规则创建的那天，所以要-1
        programCalendar.set(Calendar.HOUR_OF_DAY, 0);         //以0点为参照坐标
        programCalendar.set(Calendar.MINUTE, 0);
        programCalendar.set(Calendar.SECOND, 0);
        Long programDayMill = programCalendar.getTimeInMillis();
//        filters.add(new PropertyFilter("EQI_status", CarouselBase.STATUS_NORMAL + ""));  //正常状态
//        filters.add(new PropertyFilter("LTT_extendTime", new Timestamp(programDayMill) + "")); //节目当天未生成过
        Criteria criteria = carouselBaseDao.createCriteria();
        criteria.add(Restrictions.eq("status", CarouselBase.STATUS_NORMAL));
        criteria.add(Restrictions.lt("extendTime", new Timestamp(programDayMill)));
        criteria.add(Restrictions.lt("errorCount", MAX_ERROR_COUNT));
        criteria.add(Restrictions.or(Restrictions.eq("repeatedStatus", 1), Restrictions.ge("endTime", new Timestamp(programDayMill))));
        criteria.setMaxResults(ONCE_FIND);
        List<CarouselBase> queryList = carouselBaseDao.find(criteria);
        List<CarouselBase> filterList = new ArrayList<CarouselBase>();

        for (CarouselBase base : queryList) {
            if (base.checkIsRepeat()) {
                filterList.add(base);
            } else if (base.getEndTime().getTime() >= current) {
                filterList.add(base);
            } else {
                addErrorCount(base);
            }
        }
        return filterList;
    }

    @Override
    public Boolean checkRuleExpire(CarouselBase carouselBase, Integer daysLeft) {

        if (carouselBase.checkIsRepeat()) {//循环规则无结束时间
            return Boolean.FALSE;
        }
        Timestamp endTime = carouselBase.getEndTime();
        Long ruleExpire = endTime.getTime();
        long currentTime = System.currentTimeMillis();
        Boolean needRemind = currentTime < ruleExpire && ruleExpire < currentTime + 3 * DateTimeUtil.DAY_TIME;
        if (needRemind) {
            try {
                sendRuleExpireRemindMail(carouselBase, daysLeft);
            } catch (Exception e) {
                logger.error("发送邮件失败，carouselBase id:{}", carouselBase.getId());
            }
        }
        return needRemind;
    }

    private void sendRuleExpireRemindMail(CarouselBase carouselBase, Integer daysLeft) throws Exception {
        ChannelBase channelBase = channelService.getChannelBase(carouselBase.getFatherId());
        if (channelBase == null || !channelBase.getIsEnable().equals(1)) {
            logger.info("channel id:{} is not enable. Mail Canceled.", channelBase.getId());
            return;
        }
        MailRemind newMail = getMailRemind("Carousel_RuleExpireRemindMail", carouselBase.getId() + "");
        if (!checkFrequent(newMail)) {
            logger.info("carouselBase id:{} endTime:{} Mail Canceled.", carouselBase.getId(), newMail.getEndTime());
            return;
        }
        Calendar noRemind = Calendar.getInstance();
        noRemind.add(Calendar.DATE, daysLeft);//三天内不再提醒
        String[] tos = getRecipients(carouselBase.getFatherId());
        if (tos != null && tos.length > 0) {
            final String title = "轮播台【" + channelBase.getName() + "】频道，自动规则到期提醒";
            Long endTime = carouselBase.getEndTime().getTime();
            String endDay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(endTime));
            String startDay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(carouselBase.getStartTime().getTime()));
            final String contents = "频道名称：【" + channelBase.getName() + "】频道，奇谱ID:" + channelBase.getQipuId()
                    + "<br>自动专辑：奇谱ID:" + carouselBase.getQipuId() +
                    "<br>自动规则时间：" + startDay + "~" + endDay
                    + "<br>将于" + endDay + "到期，请更新节目单"
                    + "<br><br><br>EPG直播中心---系统邮件";
            SendMailUtils.send(title, tos, contents);
            saveMailHistory(newMail, title, StringUtil.mergeToString(tos, ";"), contents, new Timestamp(noRemind.getTimeInMillis()));
        } else {
            logger.warn("收件人为空,fatherId：{}", carouselBase.getFatherId());
        }

    }

    private void sendAlbumUpdateRemindMail(CarouselBase carouselBase, ProgramData programData) throws Exception {
        ChannelBase channelBase = channelService.getChannelBase(carouselBase.getFatherId());
        if (channelBase == null || !channelBase.getIsEnable().equals(1)) {
            logger.info("channel id:{} is not enable. Mail Canceled.", channelBase.getId());
            return;
        }
        MailRemind newMail = getMailRemind("Carousel_AlbumUpdateRemindMail", carouselBase.getId() + "");
        Map<String, String> map = new HashMap<String, String>();
        map.put(MailConstant.C_ID, "" + channelBase.getId());
        map.put(MailConstant.C_NAME, channelBase.getName());
        map.put(MailConstant.C_QIPU_ID, "" + channelBase.getQipuId());
        map.put(MailConstant.CAROUSEL_QIPU_ID, "" + carouselBase.getQipuId());
        map.put("startDay", DateTimeUtil.transLongFormat(carouselBase.getStartTime().getTime()));
        map.put("endDay", DateTimeUtil.transLongFormat(carouselBase.getEndTime().getTime()));
        map.put(MailConstant.P_ID, programData.getId() + "");
        map.put(MailConstant.P_NAME, programData.getPname());
        map.put(MailConstant.P_QIPU_ID, programData.getQipuId() + "");
        map.put("programStartTime", DateTimeUtil.transLongFormat(programData.getStartPlayTime().getTime()));
        map.put("programEndTime", DateTimeUtil.transLongFormat(programData.getStopPlayTime().getTime()));
        notifyMailService.addMailAsyncTask(MailConstant.TOS_TYPE_FROM_LOG, MailContentUtil.ContentType.LB_ALBUM_UPDATE_REMIND.getTypeIndex(), map);
        saveMailHistory(newMail, MailContentUtil.ContentType.LB_ALBUM_UPDATE_REMIND.toString(), "", "", null);

    }

    private boolean checkFrequent(MailRemind mailRemind) {
        Timestamp endRange = mailRemind.getEndTime();
        Timestamp now = new Timestamp(System.currentTimeMillis());
        if (endRange != null && now.before(endRange)) {
            return false;
        }
        return true;
    }

    private void saveMailHistory(MailRemind newMail, String title, String tos, String contents, Timestamp noRemind) {
        newMail.setTitle(title);
        newMail.setContent(contents);
        newMail.setTos(tos);
        if (noRemind != null) {
            newMail.setEndTime(noRemind);
        }
        try {
            mailRemindDao.save(newMail);
        } catch (Exception e) {
            logger.error("save MailRemind error,id:" + newMail.getId(), e);
        }
    }

    private MailRemind getMailRemind(String type, String entityId) {
        List<PropertyFilter> filters = Lists.newArrayList();
        filters.add(new PropertyFilter("EQS_type", type));
        filters.add(new PropertyFilter("EQS_entityId", entityId));
        List<MailRemind> mails = mailRemindDao.find(filters);
        if (mails.size() > 0) {
            return mails.get(0);
        } else {
            MailRemind newMail = new MailRemind();
            newMail.setType(type);
            newMail.setEntityId(entityId);
            return newMail;
        }
    }

    private String[] getRecipients(Long fatherId) {
        String[] tos = notifyMailService.getTos(fatherId, 0, MailConstant.TOS_TYPE_FROM_LOG);
        return tos;
    }

    @Override
    public List<CarouselBase> getCarouselToFollow() {
//        List<CarouselBase> retList = Lists.newArrayList();
        List<PropertyFilter> filters = Lists.newArrayList();
        Long currentMill = System.currentTimeMillis();
        filters.add(new PropertyFilter("EQI_status", CarouselBase.STATUS_NORMAL + ""));  //正常状态
//        filters.add(new PropertyFilter("LTL_copyrightEndTime",currentMill+"")); //版权未到期
        filters.add(new PropertyFilter("EQI_followedStatus", CarouselBase.FOLLOW_STATUS_YES + "")); //跟播状态
//        filters.add(new PropertyFilter("GTT_endTime",new Timestamp(currentMill)+"")); //未结束
        List<CarouselBase> queryList = carouselBaseDao.find(filters);
//        for (CarouselBase single : queryList) {
//            Long copyEnd = single.getCopyrightEndTime();
//            if (copyEnd.equals(-100L) || currentMill < copyEnd) {   //版权无过期时间或未过期
////                retList.add(single);
//            } else {
//                logger.warn("CarouselBase id:{}版权已过期", single.getId());
//            }
//        }
        return queryList;
    }

    @Override
    public void updateFollowQipuId(CarouselBase carouselBase) {
        if (!carouselBase.checkIsFollow()
                || carouselBase.getTotalNum() == carouselBase.getLastCreatedNo()) { //目前没有 跟播+循环的组合，跟播的创建只用预占位方式。
            return;
        }
//        if (!carouselBase.checkIsFollow()
//                || carouselBase.getTotalNum() == carouselBase.getActualNum()) { //非跟播状态或已更新完毕
//            return;
//        }
        List<ProgramData> programToFollow = queryFollowProgram(carouselBase);
        if (programToFollow.isEmpty()) {
            return;
        }
        if (programToFollow.size() >= VALID_NUM) {
            //to do
            logger.warn("invalid follow data, carouselBaseId:" + carouselBase.getId() + ";live_episode_Id:" + carouselBase.getFatherId());
            return;
        }
        SearchSettingConfig config = channelService.getSearchConfig(carouselBase.getFatherId());
        List<Long> albumVideoIdList = queryQipuIdFromAlbum(carouselBase.getQipuId(), config);
        if (albumVideoIdList == null || albumVideoIdList.isEmpty()) {
            logger.warn("专辑未取到数据,qipuId:{}", carouselBase.getQipuId());
            return;
        }
        Integer albumSize = albumVideoIdList.size();
        Integer actualSize = carouselBase.getActualNum();
        Integer totalNum = carouselBase.getTotalNum();
        if (totalNum < albumSize) {
            logger.info("carouselBase id:{},albumSize gt totalNum. albumSize:{},totalNum:{} ,set albumSize = totalNum", carouselBase.getId(), albumSize, totalNum);
            albumSize = totalNum;
        }
        if (albumSize > actualSize && albumSize > 0) {  //专辑有更新
            logger.info("carouselBase id:{},actual num has changed. old:{},new:{}", carouselBase.getId(), actualSize, albumSize);
            carouselBase.setActualNum(albumSize);
            carouselBase.setNeedUpdate(true);
        }

        List<ProgramData> programToSave = new ArrayList<ProgramData>();
        List<Long> illegalList = new ArrayList<Long>();
        List<Long> checkModificationList = new ArrayList<Long>();
        for (ProgramData followProgram : programToFollow) {
            Integer lastEpisode = carouselBase.getLastCreatedNo();
            Integer newEpisode = null;
            if (lastEpisode == 0) {
                newEpisode = carouselBase.getStartNum();
            } else {
                newEpisode = lastEpisode + 1;
            }


            logger.info("carouselBase id:{},newEpisode:{},totalNum:{},albumSize:{}", carouselBase.getId(), newEpisode, totalNum, albumSize);
            if (newEpisode <= albumSize) {
                Long qipuId = albumVideoIdList.get(newEpisode - 1);
                carouselBase.setLastCreatedNo(newEpisode);
                carouselBase.setLastCreatedQipuId(qipuId);
                fillFollowProgram(followProgram, qipuId);
                programToSave.add(followProgram);
                if (followProgram.checkIsIllegal()) {
                    illegalList.add(qipuId);
                } else {
                    checkModificationList.add(qipuId);
                }

            } else if (newEpisode > albumSize && newEpisode <= totalNum) {  //专辑还没更新
                logger.info("专辑还未更新，无法更新当前预占位.carouselBase id:{}", carouselBase.getId());
                Integer minutesLeft = minutesToPlay(followProgram.getStartPlayTime());
                Integer standard = new Integer(Configuration.getString("mail.followUpdate.remind.delay", "120"));
                if (minutesLeft > 0 && minutesLeft < standard) {
                    try {
                        logger.info("[CarouselServiceImpl][updateFollowQipuId][sendEmail - start]");
                        sendAlbumUpdateRemindMail(carouselBase, followProgram);
                        logger.info("[CarouselServiceImpl][updateFollowQipuId][sendEmail - end]");
                        followProgram.setIllegalStatus(ProgramData.ILLEGAL_STATUS_ERROR);
                        carouselBase.setLastCreatedNo(newEpisode);
                        carouselBase.setLastCreatedQipuId(0);
                        programToSave.add(followProgram);
                        logger.info("[CarouselServiceImpl][illegal flag skip[  {}][carouselId={}]", newEpisode, carouselBase.getId());
                    } catch (Exception e) {
                        logger.error("发送邮件失败，carouselBase id:{}", carouselBase.getId());
                    }
                }

                break;
            } else {   //剧集已播完，看是否需要循环播放
                if (carouselBase.checkIsRepeat()) {
                    newEpisode -= totalNum;
                    Long qipuId = albumVideoIdList.get(newEpisode - 1);
                    carouselBase.setLastCreatedNo(newEpisode);
                    carouselBase.setLastCreatedQipuId(qipuId);
                    fillFollowProgram(followProgram, qipuId);
                    programToSave.add(followProgram);
                    if (followProgram.checkIsIllegal()) {
                        illegalList.add(qipuId);
                    }
                } else {
                    logger.error("当前剧集已播完且未设置循环播放，无法更新当前预占位.carouselBase id:{}", carouselBase.getId());
                    break;
                }
            }
        }     //end loop
        if (illegalList.size() > 0) {
            ChannelBase channelBase = channelService.getChannelBase(carouselBase.getFatherId());
            Map<String, String> map = new HashMap<String, String>();
            map.put(MailConstant.C_NAME, channelBase.getName());
            map.put(MailConstant.C_QIPU_ID, "" + channelBase.getQipuId());
            map.put(MailConstant.SHOW_NAME, carouselBase.getShowName());
            map.put(MailConstant.CAROUSEL_QIPU_ID, "" + carouselBase.getQipuId());
            map.put(MailConstant.P_QIPU_IDS, illegalList.toString().replaceAll("\\[|\\]", ""));
            map.put(MailConstant.ASSIGN_TOS, carouselBase.getOpUser());
            notifyMailService.addMailAsyncTask(0, MailContentUtil.ContentType.LB_RULE_ERROR.getTypeIndex(), map);
        }

        if (saveProgramDatas(programToSave, carouselBase.getFatherId())) {
            if (checkModificationList.size() > 0) {
                if (ChannelNotifyUtil.isInList(carouselBase.getFatherId(), checkModificationList)) {
                    ChannelNotifyUtil.forceNotify(carouselBase.getFatherId());
                }
            }
            channelService.notifyHasPrograms(carouselBase.getFatherId());
//            carouselBase.setExtendTime(new Timestamp(System.currentTimeMillis()));
            carouselBaseDao.save(carouselBase);

            for (ProgramData programData : programToSave) {
                programAsyncService.addBroadCoastToQipu(programData, false);
            }
            programAsyncService.triggerOnce();
        } else {
            logger.info("保存节目信息失败.carouselBase id:{}", carouselBase.getId());
        }
    }

    private void fillFollowProgram(ProgramData followProgram, Long qipuId) {
        ProgramQipuData qipuData = ApiQipuUtil.queryByQipuIdAndCheck(qipuId, channelService.getSearchConfig(followProgram.getFatherId()));
        ProgramData original = qipuData.getOriginal();
        followProgram.setIllegalStatus(original.getIllegalStatus());
        if (!qipuData.isIllegal()) {
            followProgram.setCarouselEpisode(qipuId);
            followProgram.setReferContentType(qipuData.getReferContentType());
            followProgram.setDuration(qipuData.getDuration());
            followProgram.setPname(original.getPname());
            followProgram.setDescription(original.getDescription());
            followProgram.setReferContentType(original.getReferContentType());
            followProgram.setDefaultLogoUrl(original.getDefaultLogoUrl());
        } else {
            logger.error("qipuId:{} illegal", qipuId);
        }
    }

    private boolean saveProgramDatas(List<ProgramData> saveList, long channelId) {
        int addLength = saveList.size();
        if (addLength > 0) {
            List<Long> qipuIdList = qipuService.registerQipuId(23, addLength);
            ProgramData entity = null;
            ChannelBase tempBase = channelService.getChannelBase(channelId);
            String formatString = getFormatString(tempBase);
            for (int i = 0; i < addLength; i++) {
                entity = saveList.get(i);
                if (entity.checkIsIllegal()) {
                    continue;
                }
                entity.setQipuId(qipuIdList.get(i));
                if (formatString != null) {
                    entity.setPageUrl(String.format(formatString, UrlGeneratorUtil.generateUrl(entity.getQipuId())));
                }
            }
        }
        for (ProgramData temp : saveList) {
            ProgramData programDb = programDataDao.get(temp.getId());
            programDb.setCarouselEpisode(temp.getCarouselEpisode());
            programDb.setDuration(temp.getDuration());
            programDb.setPname(temp.getPname());
            programDb.setReferContentType(temp.getReferContentType());
            programDb.setDescription(temp.getDescription());
            programDb.setDefaultLogoUrl(temp.getDefaultLogoUrl());
            programDb.setQipuId(temp.getQipuId());
            programDb.setPageUrl(temp.getPageUrl());
            programDb.setIllegalStatus(temp.getIllegalStatus());
            programDataDao.save(programDb);
        }
        return true;

    }

    private List<ProgramData> queryFollowProgram(CarouselBase carouselBase) {
        Calendar calendar = Calendar.getInstance();
//        String currentDay = DateTimeUtil.getCurrentDay();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startTime = sdf.format(new Date(calendar.getTimeInMillis()));
        calendar.add(Calendar.DATE, 1);
        String endTime = sdf.format(new Date(calendar.getTimeInMillis()));
        List<PropertyFilter> filters = Lists.newArrayList();
        filters.add(new PropertyFilter("EQI_deleteFlag", "0"));//未删除
        filters.add(new PropertyFilter("GTT_startPlayTime", startTime));//开播时间在24小时内
        filters.add(new PropertyFilter("LTT_startPlayTime", endTime));//开播时间在24小时内
        filters.add(new PropertyFilter("EQL_qipuId", "0"));//预占位
        filters.add(new PropertyFilter("EQI_illegalStatus", "0"));// 合法性。
//        filters.add(new PropertyFilter("EQI_fatherId", carouselBase.getFatherId()+""));//频道
        filters.add(new PropertyFilter("EQI_ruleId", carouselBase.getId() + "")); //规则ID
        return programDataDao.find(filters, Order.asc("startPlayTime"));
    }

    private List<Long> queryQipuIdFromAlbum(Long qipuId, SearchSettingConfig config) {
        return ApiLegoUtil.getEpisodeChildren(qipuId, config);
    }

    /*
        还有多少分钟开播，默认提前两小时邮件提醒
         */
    private Integer minutesToPlay(Timestamp startPlayTime) {
        Long start = startPlayTime.getTime();
        Long now = System.currentTimeMillis();
        return new Integer((start - now) / 1000 / 60 + "");
    }

    /*2.2.0*/

    @Override
    public List<AutoCarouselEpisodeRule> getCarouselAutomationList(long channelId) {
        Criteria criteria = carouselAutomationDao.createCriteria();
        criteria.add(Restrictions.gt("status", AutoCarouselEpisodeRule.STATUS_DELETE));
        criteria.add(Restrictions.eq("fatherId", channelId));
        criteria.addOrder(Order.asc("execIndex"));
        criteria.addOrder(Order.desc("lastSortTime"));
        return carouselAutomationDao.find(criteria);
    }


    @Override
    public List<AutoCarouselEpisodeLibrary> getCarouselAutomationProgramList(long channelId) {
        Criteria criteria = autoCarouselEpisodeLibraryDao.createCriteria();
        criteria.add(Restrictions.gt("status", AutoCarouselEpisodeLibrary.STATUS_DELETE));
        criteria.add(Restrictions.eq("fatherId", channelId));
        criteria.addOrder(Order.asc("execIndex"));
        criteria.addOrder(Order.desc("lastSortTime"));
        return autoCarouselEpisodeLibraryDao.find(criteria);
    }

    @Override
    public AutoCarouselEpisodeRule getByAutomationId(long entityId) {
        return carouselAutomationDao.findUniqueBy("id", entityId);
    }

    @Override
    public AutoCarouselEpisodeLibrary getByEpisodeLibraryId(long entityId) {
        return autoCarouselEpisodeLibraryDao.findUniqueBy("id", entityId);
    }

    @Override
    public void saveAutomation(AutoCarouselEpisodeRule entity) {
        carouselAutomationDao.save(entity);
    }


    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void updateAutomationList(List<AutoCarouselEpisodeRule> saveList) {
        /*保存 记录 */
        Session session = carouselAutomationDao.getSessionFactory().openSession();
        Transaction transaction = session.beginTransaction();
        try {
            for (AutoCarouselEpisodeRule temp : saveList) {
                session.update(temp);
            }
            transaction.commit();
        } catch (Exception e) {
            logger.error(e.getMessage());
            transaction.rollback();
        } finally {
            session.close();
        }
    }

    @Override
    public void saveCarouselEpisode(AutoCarouselEpisodeLibrary entity) {
        autoCarouselEpisodeLibraryDao.save(entity);
    }

    @Override
    public boolean delAutomation(String ids) {
        logger.info("[changeAutomationStatus][ids={}]", ids);
        String sql = "update autocarousel_episode_rule set status= -1 where id in (" + ids + ")";
        int executeUpdate = 0;
        try {
            executeUpdate = carouselAutomationDao.getSession().createSQLQuery(sql).executeUpdate();
        } catch (Exception e) {
            logger.info("[CarouselServiceImpl][delAutomation][Exception={}]", e);
            return false;
        }
        logger.info("[CarouselServiceImpl][delAutomation][executeUpdate={}]", executeUpdate);
        return true;
    }

    @Override
    public int indexMoveAutomation(long selId, long aimId) {
        return indexMoveIIndexSort(selId, aimId, carouselAutomationDao);
    }

    @Override
    public int indexMoveEpisode(long selId, long aimId) {
        return indexMoveIIndexSort(selId, aimId, autoCarouselEpisodeLibraryDao);
    }

    public int indexMoveIIndexSort(long selId, long aimId, IIndexSortDao baseDao) {
        logger.info("[indexMoveIIndexSort][selId={}][aimId={}][class={}]", selId, aimId, baseDao.getClass());
        IIndexSort rule1 = baseDao.getById(selId);
        if (rule1 == null) {
            return -1;
        }
        List<? extends IIndexSort> ruleList = baseDao.getListByFatherId(rule1.getFatherId());
        IIndexSort rule2 = null;
        for (IIndexSort temp : ruleList) {
            if (temp.getId() == aimId) {
                rule2 = temp;
                break;
            }
        }
        if (rule2 == null) {
            return -1;
        }
        if (rule1.getStatus() == -1 || rule2.getStatus() == -1) {
            return -2;
        }
        if (rule2.getId() == rule1.getId()) {
            return 1;
        }
        rule1.setExecIndex(rule2.getExecIndex());
        int aimIndex = ruleList.indexOf(rule2);
        adjustIndex(rule1, aimIndex, ruleList, baseDao);
        return 1;
    }

    @Override
    public int indexMoveAutomationByIndex(long selId, int aimIndex) {
        return indexMoveIIndexSortByIndex(selId, aimIndex, carouselAutomationDao);
    }

    @Override
    public int indexMoveEpisodeByIndex(long selId, int aimIndex) {
        return indexMoveIIndexSortByIndex(selId, aimIndex, autoCarouselEpisodeLibraryDao);
    }

    public int indexMoveIIndexSortByIndex(long selId, int aimIndex, IIndexSortDao baseDao) {
        logger.info("[indexMoveIIndexSortByIndex][selId={}][selIndex={}][aimIndex={}][class={}]", selId, aimIndex, baseDao.getClass());
        IIndexSort rule1 = baseDao.getById(selId);
        if (rule1 == null) {
            return -1;
        }

        List<? extends IIndexSort> ruleList = baseDao.getListByFatherId(rule1.getFatherId());
        int totalLength = ruleList.size();
        if( totalLength == 1){
            return 1;
        }
        IIndexSort rule2 = null;
        if (aimIndex >= totalLength) {
            rule2 =  ruleList.get( totalLength -1 );
            if( rule2.getId() == rule1.getId() ){
                return 1;
            }
            rule1.setExecIndex(rule2.getExecIndex()+1);
            baseDao.saveEntity(rule1);
        }else{
             rule2 = ruleList.get(aimIndex);
            if (rule2 == null) {
                return -1;
            }
            if (rule1.getStatus() == -1 || rule2.getStatus() == -1) {
                return -2;
            }
            if (rule2.getId() == rule1.getId()) {
                return 1;
            }
            rule1.setExecIndex(rule2.getExecIndex());
            adjustIndex(rule1, aimIndex, ruleList, baseDao);
        }
        return 1;
    }

    private void adjustIndex(IIndexSort rule1, int aimIndex, List<? extends IIndexSort> ruleList, IIndexSortDao baseDao) {
        long lastSortTime = System.currentTimeMillis();
        rule1.setLastSortTime(lastSortTime);
        IIndexSort sameExecIndexRule = null;
        List<IIndexSort> updateList = new ArrayList<IIndexSort>();
        updateList.add(rule1);
        for (int i = aimIndex - 1; i > -1; i--) {
            sameExecIndexRule = ruleList.get(i);
            if (sameExecIndexRule == rule1) {
                continue;
            }
            if (sameExecIndexRule.getExecIndex() == rule1.getExecIndex()) {
                sameExecIndexRule.setLastSortTime(lastSortTime + aimIndex - i);
                updateList.add(sameExecIndexRule);
            } else {
                break;
            }
        }
        for (IIndexSort saveRule : updateList) {
            baseDao.saveEntity(saveRule);
        }
    }

    @Override
    public int changeAutomationStatus(long channelId, int execStatusNew, int execStatusOld) {
        logger.info("[changeAutomationStatus][channelId={}][execStatusNew={}][execStatusOld={}]", channelId, execStatusNew, execStatusOld);
        String hql = "update AutoCarouselEpisodeRule set status = ? where fatherId = ? and status = ? ";
        int executeUpdate = carouselAutomationDao.createQuery(hql).setInteger(0, execStatusNew).setLong(1, channelId).setInteger(2, execStatusOld).executeUpdate();
        logger.info("[CarouselServiceImpl][changeAutomationStatus][executeUpdate={}]", executeUpdate);
        if (executeUpdate > 0) {
            return 1;
        } else if (execStatusNew > 0) {
            return -1;
        } else {
            return 0;
        }
    }

    @Override
    public List<TCarouselAutomation> transToTCarouselAutomationList(List<AutoCarouselEpisodeRule> dataList) {
        List<TCarouselAutomation> resultList = new ArrayList<>();
        TCarouselAutomation tCarouselAutomation = null;
        if (dataList != null) {
            for (AutoCarouselEpisodeRule tempData : dataList) {
                tCarouselAutomation = transToTCarouselAutomation(tempData);
                resultList.add(tCarouselAutomation);
            }
        }
        return resultList;
    }

    @Override
    public TCarouselAutomation transToTCarouselAutomation(AutoCarouselEpisodeRule tempData) {
        TCarouselAutomation _TCarouselAutomation = new TCarouselAutomation();
        _TCarouselAutomation.setId(tempData.getId());
        _TCarouselAutomation.setFatherId(tempData.getFatherId());
        _TCarouselAutomation.setQipuId(tempData.getQipuId());
        _TCarouselAutomation.setStatus(tempData.getStatus());
        _TCarouselAutomation.setShowName(tempData.getShowName());
        _TCarouselAutomation.setActualNum(tempData.getActualNum());
        _TCarouselAutomation.setTotalNum(tempData.getTotalNum());
        _TCarouselAutomation.setChannelTag(tempData.getChannelTag());
        _TCarouselAutomation.setBossStatus(tempData.getBossStatus());
        _TCarouselAutomation.setPublishStatus(tempData.getPublishStatus());
        _TCarouselAutomation.setOpUser(tempData.getOpUser());
        _TCarouselAutomation.setPageUrl(tempData.getPageUrl());
        _TCarouselAutomation.setCurrentExec(tempData.getCurrentExec());
        return _TCarouselAutomation;
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<AutoCarouselEpisodeRule> queryAutomationByCriterion(List<Criterion> list, List<Order> orderList, int maxResultNum) {
        Criteria criteria = programDataDao.createCriteria();
        for (Criterion temp : list) {
            criteria.add(temp);
        }
        if (maxResultNum != -1) {
            criteria.setMaxResults(maxResultNum);
        }

        if (orderList != null && !orderList.isEmpty()) {
            for (Order order : orderList) {
                criteria.addOrder(order);
            }
        }
        return carouselAutomationDao.find(criteria);
    }

    @Override
    public List<AutoCarouselEpisodeRule> getAutomationToUpdate() {
        String sql = "select {outRule.*} from (SELECT rule.* from autocarousel_episode_rule rule WHERE rule.`status` !=-1 order by rule.execute_time desc) outRule GROUP BY outRule.father_id";
//        Criteria criteria = carouselAutomationDao.createCriteria();
//        criteria.add(Restrictions.eq("status",1));
//        criteria.setProjection(Projections.groupProperty("fatherId"));
//        criteria.addOrder(Order.desc("fatherId"));
//        criteria.addOrder(Order.desc("executeTime"));
        return carouselAutomationDao.getSession().createSQLQuery(sql).addEntity("outRule", AutoCarouselEpisodeRule.class).list();
    }

    @Override
    public int resetAutomation(long channelId) {
        int executeNum = 0;
        String hql = "update AutoCarouselEpisodeRule set lastCreatedNo = 0,currentExec = 0,executeTime = ? where fatherId = ? and status > -1 ";
        executeNum = carouselAutomationDao.createQuery(hql)
                .setTimestamp(0, new Timestamp(System.currentTimeMillis()))
                .setLong(1, channelId)
                .executeUpdate();
        logger.info("[resetAutomation] cid={}, executeNum={}", channelId, executeNum);
        return executeNum;
    }

    @Override
    public TCarouselEpisodePage getCarouselEpisodeList(TPage page, TFilter filter) {
        List<PropertyFilter> filterList = TransUtil.builderFilterList(filter);
        Page<AutoCarouselEpisodeLibrary> dbPage = TransUtil.buildPage(page);
        Page<AutoCarouselEpisodeLibrary> pageData = queryCarouselEpisodeByFilters(dbPage, filterList);
        List<AutoCarouselEpisodeLibrary> dataList = pageData.getResult();
        TCarouselEpisodePage carouselEpisodePage = new TCarouselEpisodePage();
        page.setTotalCount(pageData.getTotalCount());
        carouselEpisodePage.setTPage(page);

        List<TCarouselEpisode> tCarouselList = transToTCarouselEpisodeList(dataList);
        carouselEpisodePage.setResult(tCarouselList);
        return carouselEpisodePage;
    }

    @Override
    public boolean saveAutoProgramDataList(List<AutoCarouselEpisodeLibrary> saveList, long channelId) {
        Session session = autoCarouselEpisodeLibraryDao.getSessionFactory().openSession();
        Transaction transaction = session.beginTransaction();
        try {
            for (AutoCarouselEpisodeLibrary temp : saveList) {
                session.saveOrUpdate(temp);
            }
            transaction.commit();
        } catch (Exception e) {
            logger.error("[channelId=" + channelId + "] " + e.getMessage());
            transaction.rollback();
            return false;
        } finally {
            session.close();
            return true;
        }
    }

    @Override
    public List<Long> queryHasQipuIds(long channelId, List<Long> qipuIds) {
        Criteria criteria = autoCarouselEpisodeLibraryDao.createCriteria();
        criteria.add(Restrictions.gt("status", AutoCarouselEpisodeLibrary.STATUS_DELETE));
        criteria.add(Restrictions.eq("fatherId", channelId));
        criteria.add(Restrictions.in("qipuId", qipuIds));
        List<AutoCarouselEpisodeLibrary> list = autoCarouselEpisodeLibraryDao.find(criteria);
        List<Long> hasList = new ArrayList<>();
        if (list != null && list.size() > 0) {
            for (AutoCarouselEpisodeLibrary temp : list) {
                hasList.add(temp.getQipuId());
            }
        }
        return hasList;
    }

    @Override
    public boolean delCarouselEpisode(String ids) {
        logger.info("[changeAutomationStatus][ids={}]", ids);
        String sql = "update autocarousel_episode_library set status= -1 where id in (" + ids + ")";
        int executeUpdate = 0;
        try {
            executeUpdate = autoCarouselEpisodeLibraryDao.getSession().createSQLQuery(sql).executeUpdate();
        } catch (Exception e) {
            logger.info("[CarouselServiceImpl][delAutomation][Exception={}]", e);
            return false;
        }
        logger.info("[CarouselServiceImpl][delAutomation][executeUpdate={}]", executeUpdate);
        return true;
    }

    @Override
    public boolean isLibraryRuleRunning(long channelId) {
        String sql = "select count(id) from autocarousel_episode_rule where father_id =? and status > 0";
        long executeUpdate = 0;
        try {
            SQLQuery query = carouselAutomationDao.getSession().createSQLQuery(sql);
            Object obj = query.setLong((0), channelId).uniqueResult();
            executeUpdate = Long.valueOf(obj + "");
        } catch (Exception e) {
            logger.info("[CarouselServiceImpl][isLibraryRuleRunning][Exception={}]", e);
            return false;
        }
        return executeUpdate > 0;
    }


    @Override
    public boolean hasEpisodeLibraryByChannelId(long channelId) {
        String sql = "select count(id) from autocarousel_episode_library where father_id =? and status > -1";
        long executeUpdate = 0;
        try {
            SQLQuery query = autoCarouselEpisodeLibraryDao.getSession().createSQLQuery(sql);
            Object obj = query.setLong((0), channelId).uniqueResult();
            executeUpdate = Long.valueOf(obj + "");
        } catch (Exception e) {
            logger.info("[CarouselServiceImpl][hasEpisodeLibraryByChannelId][Exception={}]", e);
            return false;
        }
        return executeUpdate > 0;
    }
}

